﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls.DataVisualization.Charting;
using ClassifierLib;
using ClassifierLib.Classifier;
using System.Windows;
using System.Collections.ObjectModel;

namespace ClassifierUI
{

    /// <summary>
    /// This class is responsible for generating data points for graph
    /// Some UI classes are mixed in here
    /// </summary>
    public class StatisticsProvider
    {
        private LCS _lcs;
        private int _iterationsBeforeTest;
        private double _count;
        private double[] _totals;
        private int _iteration;

        private List<LineSeries> _lines = new List<LineSeries>();
        private List<ObservableCollection<Point>> _lineData = new List<ObservableCollection<Point>>();

        /// <summary>
        /// List of lines to display on chart (which are databound to corresponding LineData structure)
        /// </summary>
        public List<LineSeries> Lines
        {
            get
            {
                return _lines;
            }
        }

        /// <summary>
        /// This is a list of collections of points for each line in graph
        /// </summary>
        public List<ObservableCollection<Point>> LineData
        {
            get
            {
                return _lineData;
            }
        }

        /// <summary>
        /// Reference to chart control on UI
        /// </summary>
        public Chart Chart
        {
            get;
            set;
        }

        /// <summary>
        /// Number of iterations before testing is done
        /// </summary>
        public int IterationsBeforeTest
        {
            get
            {
                return _iterationsBeforeTest;
            }
        }


        private int iterationsLeft;

        public StatisticsProvider(LCS system, int iterationsBeforeTest)
        {
            _lcs = system;
            _lcs.IterationComplete += lcs_IterationComplete;
            _lcs.TrainingStarted += lcs_TrainingStarted;
            _lcs.TrainingEnded += lcs_TrainingFinished;
            _iterationsBeforeTest = iterationsBeforeTest;
            iterationsLeft = _iterationsBeforeTest;

        }

        private void lcs_IterationComplete(object sender, IterationArgs args)
        {
            _iteration++;
            if (iterationsLeft > 0)
            {
                iterationsLeft--;
            }
            else
            {
                iterationsLeft = IterationsBeforeTest;

                //TODO do stats here
                FindAverageValues(args.Population);
            }

        }

        private delegate void ParameterlessDelegate();

        //TODO: this is VERY messy and should be refactored in next version
        private void lcs_TrainingStarted(object sender, EventArgs args)
        {
            // clear previous results
            _iteration = 0;
            Lines.Clear();
            LineData.Clear();
            Chart.Series.Clear();

            // add an empty line for each entry in initialstatistics
            for (int i = 0; i < Rule.InitialStatistics.Values.Count; i++)
            {
                LineData.Add(new ObservableCollection<Point>());
            }

            ParameterlessDelegate d = delegate
            {
                Chart.ContextMenu = new System.Windows.Controls.ContextMenu();

                // the following code constructs the lines for the chart, and has to execute on the UI thread
                for (int i = 0; i < Rule.InitialStatistics.Values.Count; i++)
                {
                    var ls = new LineSeries();
                    Lines.Add(ls);
                    Chart.Series.Add(ls);

                    ls.Title = Rule.InitialStatistics.Keys.ToArray()[i];

                    ls.IndependentValuePath = "Y";
                    ls.DependentValuePath = "X";
                    ls.ItemsSource = LineData[i];
                    
                    // now add context menu item
                    var cmb = new System.Windows.Controls.CheckBox();
                    cmb.Click += cmb_click;
                    cmb.Tag = ls;
                    cmb.IsChecked = true;
                    cmb.Content = ls.Title;
                    Chart.ContextMenu.Items.Add(cmb);
                }

                //Addind LCS Performance measure for charting - done differently to other stats as it is
                //a measure of the overall performance rather than specific to a rule
                LineData.Add(new ObservableCollection<Point>());
                var lseries = new LineSeries();
                Lines.Add(lseries);
                Chart.Series.Add(lseries);

                lseries.Title = "LCS Performance";

                lseries.IndependentValuePath = "Y";
                lseries.DependentValuePath = "X";
                lseries.ItemsSource = LineData[LineData.Count-1];

                // now add context menu item
                var cmb2 = new System.Windows.Controls.CheckBox();
                cmb2.Click += cmb_click;
                cmb2.Tag = lseries;
                cmb2.IsChecked = true;
                cmb2.Content = lseries.Title;
                Chart.ContextMenu.Items.Add(cmb2);


    
            };

            
            Chart.Dispatcher.BeginInvoke(d, null);

            

        }

        private void cmb_click(object sender, RoutedEventArgs e)
        {
            System.Windows.Controls.CheckBox button = sender as System.Windows.Controls.CheckBox;
            LineSeries ls = button.Tag as LineSeries;

            if (Chart.Series.Contains(ls))
            {
                Chart.Series.Remove(ls);
                button.IsChecked = false;
            }
            else
            {
                Chart.Series.Add(ls);
                button.IsChecked = true;
            }
        }
        private void lcs_TrainingFinished(object sender, EventArgs args)
        {
           
        }

        private void FindAverageValues(Population p)
        {
            lock (p)
            {
                // create new array large enough to store all values
                _totals = new double[Rule.InitialStatistics.Values.Count];
                _count = p.Count();

                foreach (Rule rule in p)
                {
                    double[] values = rule.Statistics.Values.ToArray();
                    for (int i = 0; i < values.Length; i++)
                    {
                        _totals[i] += values[i];
                    }
                }
            }

            // divide through by number of records
            for (int i = 0; i < _totals.Length; i++)
            {
                _totals[i] /= _count;
            }

            //For some unknown reason LineData observablecollection cannot be accessed in worker thread despite being created there, may be artifact of setting ItemsSource on grid
            ParameterlessDelegate d = delegate
            {
                // add to array of datapoints
                for (int i = 0; i < _totals.Length; i++)
                {
                    if (Double.IsInfinity(_totals[i]))
                    {
                        int x = 0;
                        x++;
                    }
                    LineData[i].Add(new Point(_totals[i], _iteration));

                }

                //BPH add total LCS performance here
                LineData[LineData.Count - 1].Add(new Point(_lcs.getLCSPerformance, _iteration));
            };

            Chart.Dispatcher.Invoke(d, null);  
        }
    }
}
